/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.cloud.loadbalancer.service;

import com.iwindplus.boot.domain.constant.CommonConstant.ExceptionConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import reactor.core.publisher.Mono;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * 处理在响应式框架中使用openFeign导致block()/blockFirst()/blockLast() are blocking, which is not supported的问题.
 *
 * @author zengdegui
 * @since 2020/4/21
 */
@Slf4j
public class CustomBlockingLoadBalancerClient extends BlockingLoadBalancerClient {
    private final ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory;

    public CustomBlockingLoadBalancerClient(LoadBalancerClientFactory loadBalancerClientFactory) {
        super(loadBalancerClientFactory);
        this.loadBalancerClientFactory = loadBalancerClientFactory;
    }

    @Override
    public <T> ServiceInstance choose(String serviceId, Request<T> request) {
        ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
        if (Objects.isNull(loadBalancer)) {
            return null;
        }
        CompletableFuture<Response<ServiceInstance>> completableFuture = CompletableFuture.supplyAsync(() -> Mono.from(loadBalancer.choose(request)).block());
        Response<ServiceInstance> loadBalancerResponse = null;
        try {
            loadBalancerResponse = completableFuture.get();
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();

            log.warn(ExceptionConstant.INTERRUPTED_EXCEPTION, ex);
        } catch (ExecutionException ex) {
            log.error(ExceptionConstant.EXECUTION_EXCEPTION, ex);
        }
        if (Objects.isNull(loadBalancerResponse)) {
            return null;
        }
        return loadBalancerResponse.getServer();
    }
}
